#include <QTimer>
#include <math.h>
#include "QKinect_Imp.h"
#include "_QKinectManager.h"

// If the statement results in a error code, throw QKinectException
#define QKINECT_VERIFY(__statement__) THROW_IF_FAIL(__statement__, QKinectException)

QKinect_Imp::QKinect_Imp (int index, NUI_IMAGE_TYPE depthType,
		NUI_IMAGE_RESOLUTION depthRes, NUI_IMAGE_TYPE colorType,
		NUI_IMAGE_RESOLUTION colorRes, bool bSeatedSkeletonMode, bool bNearMode,
		bool bFallbackToDefault) throw (QKinectException) : _hDepthEvent (NULL),
		_hColorEvent (NULL), _hSkeletonEvent (NULL), _hDepthStream (NULL),
		_hColorStream (NULL), _isNuiInitialized (false), _isStarted (false),
		_pSensor (NULL), _pCoordMapper (NULL)
{
	try {
		_init (index, depthType, depthRes, colorType, colorRes,
				bSeatedSkeletonMode, bNearMode, bFallbackToDefault);
	} catch (...) {
		_release ();
		throw;
	}
}


QKinect_Imp::~QKinect_Imp ()
{
	_release ();
	QKinectManager::_removeStoppedKinect (this);
}

void QKinect_Imp::registerNewFrameCallback (QObject *pObj, const char *slot)
{
	connect (this, SIGNAL(newFrameReady(void)), pObj, slot);
}

void QKinect_Imp::start () throw (QKinectException)
{
	if (!_isNuiInitialized)
		throw QKinectException (E_NUI_NOTCONNECTED);
	_isStarted = true;
	QTimer::singleShot (30, this, SLOT(_receiveNewFrame(void)));
}

void QKinect_Imp::stop ()
{
	_isStarted = false;
}

CameraConfig QKinect_Imp::getColorConfig () const
{
	CameraConfig colorConfig;

	colorConfig.frameSize = _getNuiFrameSize (_colorFrame);
	FLOAT focalLength = 0.f;
	if (colorConfig.frameSize == QSize (1280, 960))
		colorConfig.focalLength = NUI_CAMERA_COLOR_NOMINAL_FOCAL_LENGTH_IN_PIXELS * 2.f;
	else
		colorConfig.focalLength = NUI_CAMERA_COLOR_NOMINAL_FOCAL_LENGTH_IN_PIXELS;
	return colorConfig;
}

CameraConfig QKinect_Imp::getDepthConfig () const
{
	CameraConfig depthConfig;

	depthConfig.frameSize = _getNuiFrameSize (_depthFrame);
	FLOAT focalLength = 0.f;
	if (depthConfig.frameSize == QSize (80, 60))
		depthConfig.focalLength = NUI_CAMERA_DEPTH_NOMINAL_FOCAL_LENGTH_IN_PIXELS / 4.f;
	else if (depthConfig.frameSize == QSize (640, 480))
		depthConfig.focalLength = NUI_CAMERA_DEPTH_NOMINAL_FOCAL_LENGTH_IN_PIXELS * 2.f;
	else
		depthConfig.focalLength = NUI_CAMERA_DEPTH_NOMINAL_FOCAL_LENGTH_IN_PIXELS;
	return depthConfig;
}

NUI_IMAGE_FRAME &QKinect_Imp::getColorFrame ()
{
	return _colorFrame;
}

const NUI_IMAGE_FRAME & QKinect_Imp::getColorFrame () const
{
	return _colorFrame;
}

NUI_IMAGE_FRAME &QKinect_Imp::getDepthFrame ()
{
	return _depthFrame;
}

const NUI_IMAGE_FRAME & QKinect_Imp::getDepthFrame () const
{
	return _depthFrame;
}

NUI_SKELETON_FRAME &QKinect_Imp::getSkeletonFrame ()
{
	return _skeletonFrame;
}

const NUI_SKELETON_FRAME & QKinect_Imp::getSkeletonFrame () const
{
	return _skeletonFrame;
}

QImage QKinect_Imp::getColorImage () const throw (QKinectException)
{
	QImage colorImage (_getNuiFrameSize (_colorFrame), QImage::Format_RGB32);
	NUI_LOCKED_RECT LockedRect;
	if (FAILED (_colorFrame.pFrameTexture->LockRect (0, &LockedRect, NULL, 0)) ||
			!LockedRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	memcpy (colorImage.bits (), LockedRect.pBits, colorImage.byteCount ());
	_colorFrame.pFrameTexture->UnlockRect (0);
	return colorImage;
}

QImage QKinect_Imp::getColorImageInDepthCoord () const throw (QKinectException)
{
	HRESULT hr;

	QSize depthSize = _getNuiFrameSize (_depthFrame);
	QSize colorSize = _getNuiFrameSize (_colorFrame);
	int depthPixels = depthSize.width () * depthSize.height ();
	int colorPixels = colorSize.width () * colorSize.height ();
	double colorToDepthDivisor = (double)colorSize.width () / depthSize.width ();
	std::vector< NUI_COLOR_IMAGE_POINT > colorCoordInDepth (depthPixels);

	INuiFrameTexture *pDepthTexture;
	BOOL isNearMode;
	QKINECT_VERIFY (_pSensor->NuiImageFrameGetDepthImagePixelFrameTexture (
			_hDepthStream, (NUI_IMAGE_FRAME*)&_depthFrame,
			&isNearMode, &pDepthTexture));
	NUI_LOCKED_RECT LockedDepthRect;
	if (FAILED (pDepthTexture->LockRect (0, &LockedDepthRect, NULL, 0)))
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	QKINECT_VERIFY (_pCoordMapper->MapDepthFrameToColorFrame (
			_depthFrame.eResolution, depthPixels,
			(NUI_DEPTH_IMAGE_PIXEL *)LockedDepthRect.pBits,
			_colorFrame.eImageType, _colorFrame.eResolution,
			depthPixels, &colorCoordInDepth[0]));

	QImage colorImage (colorSize, QImage::Format_RGB32);
	colorImage.fill (qRgb (0, 0, 0));
	NUI_LOCKED_RECT LockedColorRect;
	if (FAILED (_colorFrame.pFrameTexture->LockRect (0, &LockedColorRect,
			NULL, 0)) || !LockedColorRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	for (int y = 0; y < colorImage.height (); ++y) {
		uint *dstLine = (uint *)colorImage.scanLine (y);
		for (int x = 0; x < colorImage.width (); ++x) {
			// calculate index into depth array
			int depthIndex = (int)(x / colorToDepthDivisor) +
					(int)(y / colorToDepthDivisor) * depthSize.width ();

			// retrieve the depth to color mapping for the current depth pixel
			LONG colorInDepthX = colorCoordInDepth[depthIndex].x;
			LONG colorInDepthY = colorCoordInDepth[depthIndex].y;
			uint *srcLine = (uint *)&LockedColorRect
					.pBits[LockedColorRect.Pitch * colorInDepthY];

			// make sure the depth pixel maps to a valid point in color space
			if (colorInDepthX >= 0 &&
					colorInDepthX < colorImage.width () &&
					colorInDepthY >= 0 &&
					colorInDepthY < colorImage.height () &&
					((NUI_DEPTH_IMAGE_PIXEL *)LockedDepthRect.pBits)[depthIndex].depth)
				dstLine[x] = srcLine[colorInDepthX];
			else
				dstLine[x] = 0xFF000000;
		}
	}
	pDepthTexture->UnlockRect (0);
	_colorFrame.pFrameTexture->UnlockRect (0);
	return colorImage;
}

QImage QKinect_Imp::getDepthImage () const throw (QKinectException)
{
	QImage depthImage (_getNuiFrameSize (_depthFrame), QImage::Format_RGB16);
	INuiFrameTexture *pTexture = _depthFrame.pFrameTexture;
	NUI_LOCKED_RECT LockedRect;
	if (FAILED (pTexture->LockRect (0, &LockedRect, NULL, 0)) || !LockedRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	if (!depthImage.isNull ()) {
		for (int y = 0; y < depthImage.height (); ++y) {
			ushort *imgLine = (ushort *)depthImage.scanLine (y);
			ushort *srcLine = (ushort *)&LockedRect.pBits [LockedRect.Pitch * y];
			for (int x = 0; x < depthImage.width (); ++x)
				imgLine[x] =  NuiDepthPixelToDepth (srcLine[x]);
		}
	}
	pTexture->UnlockRect (0);
	return depthImage;
}

QImage QKinect_Imp::getPlayerImage () const throw (QKinectException)
{
	QImage playerImage (_getNuiFrameSize (_depthFrame), QImage::Format_Indexed8);
	INuiFrameTexture *pTexture = _depthFrame.pFrameTexture;
	NUI_LOCKED_RECT LockedRect;
	if (FAILED (pTexture->LockRect (0, &LockedRect, NULL, 0)) || !LockedRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	if (!playerImage.isNull ()) {
		for (int y = 0; y < playerImage.height (); ++y) {
			uchar *imgLine = playerImage.scanLine (y);
			ushort *srcLine = (ushort *)&LockedRect.pBits [LockedRect.Pitch * y];
			for (int x = 0; x < playerImage.width (); ++x)
				imgLine[x] = NuiDepthPixelToPlayerIndex (srcLine[x]);
		}
	}
	pTexture->UnlockRect (0);
	return playerImage;
}

QImage QKinect_Imp::getDepthWithPlayerImage () const throw (QKinectException)
{
	QImage depthImage (_getNuiFrameSize (_depthFrame), QImage::Format_RGB16);
	NUI_LOCKED_RECT LockedRect;
	if (FAILED (_depthFrame.pFrameTexture->LockRect (0, &LockedRect, NULL, 0)) ||
			!LockedRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	memcpy (depthImage.bits (), LockedRect.pBits, depthImage.byteCount ());
	_depthFrame.pFrameTexture->UnlockRect (0);
	return depthImage;
}

QImage QKinect_Imp::getDepthWithPlayerImage32 () const throw (QKinectException)
{
	QSize depthSize = _getNuiFrameSize (_depthFrame);
	QSize colorSize = _getNuiFrameSize (_colorFrame);
	int depthPixels = depthSize.width () * depthSize.height ();
	int colorPixels = colorSize.width () * colorSize.height ();
	double colorToDepthDivisor = (double)colorSize.width () / depthSize.width ();
	std::vector< NUI_DEPTH_IMAGE_POINT > depthCoordInColor (colorPixels);

	INuiFrameTexture *pDepthTexture;
	BOOL isNearMode;
	QKINECT_VERIFY (_pSensor->NuiImageFrameGetDepthImagePixelFrameTexture (
			_hDepthStream, (NUI_IMAGE_FRAME*)&_depthFrame,
			&isNearMode, &pDepthTexture));
	NUI_LOCKED_RECT LockedDepthRect;
	if (FAILED (pDepthTexture->LockRect (0, &LockedDepthRect, NULL, 0)))
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	QImage depthImage (depthSize, QImage::Format_RGB32);
	depthImage.fill (qRgb (0, 0, 0));
	memcpy (depthImage.bits (), LockedDepthRect.pBits, depthImage.byteCount ());
	_depthFrame.pFrameTexture->UnlockRect (0);
	return depthImage;
}

QImage QKinect_Imp::getDepthImageInColorCoord () const throw (QKinectException)
{
	QSize depthSize = _getNuiFrameSize (_depthFrame);
	QSize colorSize = _getNuiFrameSize (_colorFrame);
	int depthPixels = depthSize.width () * depthSize.height ();
	int colorPixels = colorSize.width () * colorSize.height ();
	double colorToDepthDivisor = (double)colorSize.width () / depthSize.width ();
	std::vector< NUI_DEPTH_IMAGE_POINT > depthCoordInColor (colorPixels);

	INuiFrameTexture *pDepthTexture;
	BOOL isNearMode;
	QKINECT_VERIFY (_pSensor->NuiImageFrameGetDepthImagePixelFrameTexture (
			_hDepthStream, (NUI_IMAGE_FRAME*)&_depthFrame,
			&isNearMode, &pDepthTexture));
	NUI_LOCKED_RECT LockedDepthRect;
	if (FAILED (pDepthTexture->LockRect (0, &LockedDepthRect, NULL, 0)))
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	QKINECT_VERIFY (_pCoordMapper->MapColorFrameToDepthFrame (
			_colorFrame.eImageType, _colorFrame.eResolution,
			_depthFrame.eResolution, depthPixels,
			(NUI_DEPTH_IMAGE_PIXEL *)LockedDepthRect.pBits,
			colorPixels, &depthCoordInColor[0]));

	QImage depthImage (depthSize, QImage::Format_RGB16);
	depthImage.fill (qRgb (0, 0, 0));
	NUI_LOCKED_RECT LockedColorRect;
	if (FAILED (_colorFrame.pFrameTexture->LockRect (0, &LockedColorRect,
			NULL, 0)) || !LockedColorRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	for (int y = 0; y < depthImage.height (); ++y) {
		ushort *dstLine = (ushort *)depthImage.scanLine (y);
		for (int x = 0; x < depthImage.width (); ++x) {
			// calculate index into depth array
			int colorIndex = (int)(x * colorToDepthDivisor) +
					(int)(y * colorToDepthDivisor) * colorSize.width ();
			dstLine[x] = depthCoordInColor[colorIndex].depth;
		}
	}
	pDepthTexture->UnlockRect (0);
	_colorFrame.pFrameTexture->UnlockRect (0);
	return depthImage;
}

QImage QKinect_Imp::getPlayerImageInColorCoord () const throw (QKinectException)
{
	QSize depthSize = _getNuiFrameSize (_depthFrame);
	QSize colorSize = _getNuiFrameSize (_colorFrame);
	int depthPixels = depthSize.width () * depthSize.height ();
	int colorPixels = colorSize.width () * colorSize.height ();
	double colorToDepthDivisor = (double)colorSize.width () / depthSize.width ();
	std::vector< NUI_DEPTH_IMAGE_POINT > depthCoordInColor (colorPixels);

	INuiFrameTexture *pDepthTexture;
	BOOL isNearMode;
	QKINECT_VERIFY (_pSensor->NuiImageFrameGetDepthImagePixelFrameTexture (
			_hDepthStream, (NUI_IMAGE_FRAME*)&_depthFrame,
			&isNearMode, &pDepthTexture));
	NUI_LOCKED_RECT LockedDepthRect;
	if (FAILED (pDepthTexture->LockRect (0, &LockedDepthRect, NULL, 0)))
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	QKINECT_VERIFY (_pCoordMapper->MapColorFrameToDepthFrame (
			_colorFrame.eImageType, _colorFrame.eResolution,
			_depthFrame.eResolution, depthPixels,
			(NUI_DEPTH_IMAGE_PIXEL *)LockedDepthRect.pBits,
			colorPixels, &depthCoordInColor[0]));

	QImage playerImage (depthSize, QImage::Format_Indexed8);
	playerImage.fill (qRgb (0, 0, 0));
	NUI_LOCKED_RECT LockedColorRect;
	if (FAILED (_colorFrame.pFrameTexture->LockRect (0, &LockedColorRect,
			NULL, 0)) || !LockedColorRect.Pitch)
		throw QKinectException (E_NUI_FRAME_NO_DATA);
	for (int y = 0; y < playerImage.height (); ++y) {
		uchar *dstLine = (uchar *)playerImage.scanLine (y);
		for (int x = 0; x < playerImage.width (); ++x) {
			// calculate index into depth array
			int colorIndex = (int)(x * colorToDepthDivisor) +
					(int)(y * colorToDepthDivisor) * colorSize.width ();
			int srcX = depthCoordInColor[colorIndex].x;
			int srcY = depthCoordInColor[colorIndex].y;
			if (srcX < 0 || srcX > depthSize.width () ||
					srcY < 0 || srcY > depthSize.height ())
				dstLine[x] = 0;
			else {
				NUI_DEPTH_IMAGE_PIXEL *srcLine = 
						(NUI_DEPTH_IMAGE_PIXEL *)&LockedDepthRect.pBits[srcY];
				dstLine[x] = srcLine[srcX].playerIndex;
			}
		}
	}
	pDepthTexture->UnlockRect (0);
	_colorFrame.pFrameTexture->UnlockRect (0);
	return playerImage;
}

std::vector< NUI_SKELETON_DATA > QKinect_Imp::getSkeletons () const
{
	std::vector< NUI_SKELETON_DATA > skeletons;
	for (int i = 0; i < NUI_SKELETON_COUNT; ++i)
		if (_skeletonFrame.SkeletonData[i].eTrackingState !=
				NUI_SKELETON_NOT_TRACKED)
			skeletons.push_back (_skeletonFrame.SkeletonData[i]);
	return skeletons;
}

std::vector< glm::ivec2 > QKinect_Imp::getSkeletonPointInColorCoord (
		const std::vector< glm::vec4 > &skeletonPoints) const
		throw (QKinectException)
{
	NUI_IMAGE_RESOLUTION colorRes = _colorFrame.eResolution;
	NUI_IMAGE_TYPE colorType = _colorFrame.eImageType;

	std::vector< glm::ivec2 > result (skeletonPoints.size ());
	for (size_t i = 0; i < skeletonPoints.size (); ++i) {
		NUI_COLOR_IMAGE_POINT p;
		QKINECT_VERIFY (_pCoordMapper->MapSkeletonPointToColorPoint (
				(Vector4 *)&skeletonPoints[i], colorType, colorRes, &p));
		result[i] = glm::ivec2 (p.x, p.y);
	}
	return result;
}

std::vector< glm::ivec3 > QKinect_Imp::getSkeletonPointInDepthCoord (
		const std::vector< glm::vec4 > &skeletonPoints) const
		throw (QKinectException)
{
	NUI_IMAGE_RESOLUTION depthRes = _depthFrame.eResolution;

	std::vector< glm::ivec3 > result (skeletonPoints.size ());
	for (size_t i = 0; i < skeletonPoints.size (); ++i) {
		NUI_DEPTH_IMAGE_POINT p;
		QKINECT_VERIFY (_pCoordMapper->MapSkeletonPointToDepthPoint (
			(Vector4 *)&skeletonPoints[i], depthRes, &p));
		result[i] = glm::ivec3 (p.x, p.y, p.depth);
	}
	return result;
}

void QKinect_Imp::_receiveNewFrame ()
{
	if (!_isStarted)
		return;
	HANDLE hEvents[] = {
		_hDepthEvent,
		_hColorEvent,
		_hSkeletonEvent
	};

	// Process signal events
	if (WAIT_OBJECT_0 ==
			WaitForSingleObject (_hDepthEvent, INFINITE))
		_updateDepthBuffer ();
	if (WAIT_OBJECT_0 ==
			WaitForSingleObject (_hColorEvent, INFINITE))
		_updateColorBuffer ();
	if (WAIT_OBJECT_0 ==
			WaitForSingleObject (_hSkeletonEvent, INFINITE))
		_updateSkeletonFrame ();
	emit newFrameReady ();
	QTimer::singleShot (30, this, SLOT(_receiveNewFrame(void)));
}

void QKinect_Imp::_init (int index, NUI_IMAGE_TYPE depthType,
		NUI_IMAGE_RESOLUTION depthRes, NUI_IMAGE_TYPE colorType,
		NUI_IMAGE_RESOLUTION colorRes, bool bSeatedSkeletonMode, bool bNearMode,
		bool bFallbackToDefault) throw (QKinectException)
{
	if (_isNuiInitialized)
		return;

	HRESULT hr = E_UNEXPECTED;

	//do not support NUI_IMAGE_TYPE_COLOR_RAW_YUV for now
	if (colorType != NUI_IMAGE_TYPE_COLOR &&
			colorType != NUI_IMAGE_TYPE_COLOR_YUV ||
			depthType != NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX &&
			depthType != NUI_IMAGE_TYPE_DEPTH)
		throw QKinectException (E_INVALIDARG);

	_hDepthEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
	_hColorEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
	_hSkeletonEvent = CreateEvent (NULL, TRUE, FALSE, NULL);

	DWORD dwNuiInitDepthFlag = (depthType == NUI_IMAGE_TYPE_DEPTH) ?
			NUI_INITIALIZE_FLAG_USES_DEPTH :
			NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX;

	QKINECT_VERIFY (NuiCreateSensorByIndex (index, &_pSensor));
	QKINECT_VERIFY (_pSensor->NuiStatus ());
	QKINECT_VERIFY (_pSensor->NuiInitialize (dwNuiInitDepthFlag |
			NUI_INITIALIZE_FLAG_USES_SKELETON |
			NUI_INITIALIZE_FLAG_USES_COLOR));
	_isNuiInitialized = true;
	DWORD dwSkeletonFlags = NUI_SKELETON_TRACKING_FLAG_ENABLE_IN_NEAR_RANGE;
	if (bSeatedSkeletonMode)
		dwSkeletonFlags |= NUI_SKELETON_TRACKING_FLAG_ENABLE_SEATED_SUPPORT;
	QKINECT_VERIFY (_pSensor->NuiSkeletonTrackingEnable (_hSkeletonEvent,
			dwSkeletonFlags));

	QKINECT_VERIFY (_pSensor->NuiImageStreamOpen (colorType, colorRes,
			0, 2, _hColorEvent, &_hColorStream));

	if (FAILED (hr = _pSensor->NuiImageStreamOpen (depthType, depthRes,
			bNearMode ? NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE : 0, 2,
			_hDepthEvent, &_hDepthStream))) {
		if (bNearMode && bFallbackToDefault) {
			hr = _pSensor->NuiImageStreamOpen (depthType, depthRes, 0, 2,
					_hDepthEvent, &_hDepthStream);
		}
		QKINECT_VERIFY (hr);
	}
	QKINECT_VERIFY (_pSensor->NuiImageStreamGetNextFrame (_hColorStream,
			1000, &_colorFrame));
	QKINECT_VERIFY (_pSensor->NuiImageStreamGetNextFrame (_hDepthStream,
			1000, &_depthFrame));
	QKINECT_VERIFY (_pSensor->NuiSkeletonGetNextFrame (1000, &_skeletonFrame));
	if (FAILED (_pSensor->NuiGetCoordinateMapper (&_pCoordMapper)))
		_pCoordMapper = NULL;
}

void QKinect_Imp::_release ()
{
	if (_isNuiInitialized)
		_pSensor->NuiShutdown ();
	if (_hSkeletonEvent && _hSkeletonEvent != INVALID_HANDLE_VALUE)
		CloseHandle (_hSkeletonEvent);
	if (_hDepthEvent && _hDepthEvent != INVALID_HANDLE_VALUE)
		CloseHandle (_hDepthEvent);
	if (_hColorEvent && _hColorEvent != INVALID_HANDLE_VALUE)
		CloseHandle (_hColorEvent);
	_hSkeletonEvent = NULL;
	_hDepthEvent = NULL;
	_hColorEvent = NULL;
	_isNuiInitialized = false;
}

void QKinect_Imp::_updateColorBuffer ()
{
	_pSensor->NuiImageStreamReleaseFrame (_hColorStream, &_colorFrame);
	_pSensor->NuiImageStreamGetNextFrame (_hColorStream, 0, &_colorFrame);
}

void QKinect_Imp::_updateDepthBuffer ()
{
	_pSensor->NuiImageStreamReleaseFrame (_hDepthStream, &_depthFrame);
	_pSensor->NuiImageStreamGetNextFrame (_hDepthStream, 0, &_depthFrame);
}

void QKinect_Imp::_updateSkeletonFrame ()
{
	_pSensor->NuiSkeletonGetNextFrame (0, &_skeletonFrame);
}

QSize QKinect_Imp::_getNuiFrameSize (const NUI_IMAGE_FRAME &frame)
{
	DWORD width, height;
	NuiImageResolutionToSize (frame.eResolution, width, height);
	return QSize (width, height);
}

INuiSensor *QKinect_Imp::getSensor ()
{
	return _pSensor;
}
